package org.jing.ext.jdbc.mybatis.param;

import org.jing.core.lang.ExceptionHandler;
import org.jing.core.util.ClassUtil;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Description: 查询参数包裹 <br>
 *
 * @author bksmate <br>
 * @since 2025-07-12 <br>
 */
public final class Params extends HashMap<String, Object> {
    private final static Map<Class<? extends BaseTypeHandler<?, ?>>, BaseTypeHandler<?, ?>> GLOBAL_HANDLER_MAP = new HashMap<>();

    private final static Map<Class<?>, BaseTypeHandler<?, ?>> GLOBAL_JAVA_MAPPING = new HashMap<>();

    private final Map<Class<? extends BaseTypeHandler<?, ?>>, BaseTypeHandler<?, ?>> handlerMap = new HashMap<>();

    private final Map<Class<?>, BaseTypeHandler<?, ?>> javaTypeMapping = new HashMap<>();

    public Params() {
        handlerMap.putAll(GLOBAL_HANDLER_MAP);
        javaTypeMapping.putAll(GLOBAL_JAVA_MAPPING);
    }

    public Params set(final Object value) {
        return set("p" + size(), value);
    }

    public Params set(final String key, final Object value) {
        ExceptionHandler.publishIfMatch(null == key, "param key cannot be null");
        Object val = value;
        if (null != val) {
            BaseTypeHandler<?, ?> typeHandler = javaTypeMapping.get(val.getClass());
            if (null != typeHandler) {
                val = typeHandler.handle(val);
            }
        }
        super.put(key, val);
        return this;
    }

    public static void enableGlobalTypeHandler(final Class<? extends BaseTypeHandler<?, ?>> clazz) {
        if (null == GLOBAL_HANDLER_MAP.get(clazz)) {
            synchronized (GLOBAL_HANDLER_MAP) {
                if (null == GLOBAL_HANDLER_MAP.get(clazz)) {
                    BaseTypeHandler<?, ?> typeHandler = ClassUtil.createInstance(clazz);
                    GLOBAL_HANDLER_MAP.put(clazz, typeHandler);
                    Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
                    Class<?> javaType = (Class<?>) types[0];
                    ExceptionHandler.publishIfMatch(GLOBAL_JAVA_MAPPING.containsKey(javaType), "java type [{}] mapping duplicated", javaType);
                    GLOBAL_JAVA_MAPPING.put(javaType, typeHandler);
                }
            }
        }
    }

    public static void disableGlobalTypeHandler(final Class<? extends BaseTypeHandler<?, ?>> clazz) {
        synchronized (GLOBAL_HANDLER_MAP) {
            Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
            Class<?> javaType = (Class<?>) types[0];
            GLOBAL_HANDLER_MAP.remove(clazz);
            GLOBAL_JAVA_MAPPING.remove(javaType);
        }
    }

    public void enableTypeHandler(final Class<? extends BaseTypeHandler<?, ?>> clazz) {
        if (null == handlerMap.get(clazz)) {
            synchronized (handlerMap) {
                if (null == handlerMap.get(clazz)) {
                    BaseTypeHandler<?, ?> typeHandler = ClassUtil.createInstance(clazz);
                    handlerMap.put(clazz, typeHandler);
                    Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
                    Class<?> javaType = (Class<?>) types[0];
                    ExceptionHandler.publishIfMatch(javaTypeMapping.containsKey(javaType), "java type [{}] mapping duplicated", javaType);
                    javaTypeMapping.put(javaType, typeHandler);
                }
            }
        }
    }

    public void disableTypeHandler(final Class<? extends BaseTypeHandler<?, ?>> clazz) {
        synchronized (handlerMap) {
            Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
            Class<?> javaType = (Class<?>) types[0];
            handlerMap.remove(clazz);
            javaTypeMapping.remove(javaType);
        }
    }

    public static void main(String[] args) {
        Params params = new Params();
        params.enableTypeHandler(DatetimeTypeHandler.class);
        params.set("123").set("456").set(new Date());
        System.out.println(params);
        params = new Params();
        params.set("123").set("456").set(new Date());
        System.out.println(params);
        Params.enableGlobalTypeHandler(DatetimeTypeHandler.class);
        params = new Params();
        params.set("123").set("456").set(new Date());
        System.out.println(params);
    }
}
